Add workspace/symbolNames and workspace/symbolInfo LSP extensions#2619
Conversation
2 similar comments
workspace/allSymbolNames and workspace/symbolInfo LSP extensionsworkspace/symbolNames and workspace/symbolInfo LSP extensions
b7f19ea to
5cd0787
Compare
58e6c3c to
41cae20
Compare
|
swiftlang/indexstore-db#287 |
|
|
||
| For each name the response contains zero or more `WorkspaceSymbolItem` values: | ||
| - Source-file symbols carry a `SymbolInformation` with a `file://` URI and the exact 0-based line/column. | ||
| - SDK/stdlib symbols carry a `WorkspaceSymbol` with `location: .uri(...)` pointing at the `file://` URI of the `.swiftinterface` or `.swiftmodule` file, with the fully-qualified module name as a `?module=` query parameter. The symbol's USR is stored in `data["usr"]`. Call `workspaceSymbol/resolve` to obtain the exact `Location` within the generated interface. The client must advertise `workspace.symbol.resolveSupport`; without it, the raw `file://` URI is returned as `SymbolInformation` instead. |
There was a problem hiding this comment.
Do we really want to make the structure of the URI part of the API contract? I would prefer to keep the URI opaque and just define that clients must call workspaceSymbol/resolve to resolve the location. This gives us more flexibility going forward.
There was a problem hiding this comment.
Some clients want to show module/group names in the candidate list. So we need to include that information somehow in the response in a way clients can read. Yes it's an API contract. We have seveal options:
- Use
.data- in generaldatashould be opaque to client. - Add dedicated (extension) field (e.g.
module) toWorkspaceSymbol- I want to avoid this at all costs. - Include module names to
WorkspaceSymbol.containerName- currently we're using this field to show nesting types, and this field is meant to be displayed as-is in the client. I didn't come up with nice format for including module names. - Use
URL- I think this is the least bad option
There was a problem hiding this comment.
I would argue that the data is more structured than the URI and I’d prefer that. Also, I personally don’t see a big issue with extending WorkspaceSymbol and would have probably done that.
If we want to go the URI route: Could we just say that the returned URI may contain a module query parameter and keep the rest of the URI opaque?
There was a problem hiding this comment.
The file path is actually a contract. Clients may look into the file path and derive the characteristics of the SDK E.g. "macOS 26.0 SDK". The way of the presentation is up-to the client so we don't want to return the exact text to show.
145c2fa to
cdff24f
Compare
|
swiftlang/indexstore-db#287 |
These two new SourceKit-LSP extension requests power "Open Quickly" in editors: the client first calls `sourcekit/workspace/symbolNames` to fetch a sorted, deduplicated list of all symbol names from the index, then calls `sourcekit/workspace/symbolInfo` with a chosen name to retrieve full symbol details (kind, location, container name, USR). For symbols located in `.swiftinterface` / `.swiftmodule` files, the response uses `WorkspaceSymbol` with a deferred location and a `data` payload (containing the USR) so the client can resolve the concrete interface position lazily via the standard `workspaceSymbol/resolve` request. Clients that do not advertise `workspace.symbol.resolveSupport` receive a plain `SymbolInformation` instead. The existing `workspace/symbol` handler is refactored to share the new `workspaceSymbolItem(for:in:copiedFileMap:)` helper. Also adds: - `Array.isSortedAndUnique` / `sortAndDedupe()` (specialised for String) to efficiently deduplicate the symbol name list - `clientSupportsWorkspaceSymbolResolve` capability check - Documentation for Open Quickly and Jump to Definition
…" and "location.range" Previously accepted any "location." prefix to accommodate VS Code, which only advertises "location.range". Now require "location" or both "location.uri" and "location.range".
|
swiftlang/indexstore-db#287 |
ahoppen
left a comment
There was a problem hiding this comment.
A few more comments, mostly in the documentation, otherwise looks good to me. Feel free to merge after addressing them.
| - Source-file symbols carry a `SymbolInformation` with a `file://` URI and the exact position. | ||
| - SDK/stdlib symbols carry a `WorkspaceSymbol` with `location: .uri(...)` pointing at the `file://` URI of the `.swiftinterface` or `.swiftmodule` file, with the fully-qualified module name as a `?module=` query parameter. The symbol's USR is stored in `data["usr"]`. Call `workspaceSymbol/resolve` to obtain the exact `Location` within the generated interface. The client must advertise `workspace.symbol.resolveSupport`; without it, the raw `file://` URI is returned as `SymbolInformation` instead. |
There was a problem hiding this comment.
As discussed offline, I would like to not make the type of URLs that are returned for specific symbols part of the API contract. Instead, we should a general sentence like:
URLs may contain a
modulequery parameter that informs the client of the name that is represented by the file.
| symbols it returns a `sourcekit-lsp://` URI and sets `range` to | ||
| the symbol's position within the generated interface (computed | ||
| server-side via `editor.find_usr`). | ||
| 2. **Content retrieval** — the client fetches the generated interface |
There was a problem hiding this comment.
Let’s highlight that this step only happens for sourcekit-lsp:// URLs.
There was a problem hiding this comment.
This document is for jump to definition to SDK symbols that has sourcekit-lsp:// scheme. I'm renaming the doc.
| 3. **Open notification** — once the client opens the tab it sends | ||
| `textDocument/didOpen` with the interface content (already fetched | ||
| in step 2) as `text`, `languageId` set to `"swift"`, and `version` | ||
| set to `1`. The server increments the ref count in |
There was a problem hiding this comment.
The client doesn’t necessarily need to set the version to 1. It just needs to be monotonically increasing as the file is modified (which it isn’t in this case). I’d just remove the mention of version here.
There was a problem hiding this comment.
I'm keeping this as-is. This is a guidance for client implementation, they need to specify some value.
| `definitionInInterface` delegates to | ||
| `SwiftLanguageService.openGeneratedInterface`, which: |
There was a problem hiding this comment.
General feedback on this document: It breaks lines very aggressively. Since the rest of the code base allows 120 columns, I think it would be good to also use 120 columns here or just not have a column limit for Markdown files at all. Makes it more readable in its raw format.
| 3. Returns `GeneratedInterfaceDetails(uri: sourcekit-lsp://..., | ||
| position: <symbol position>)`. | ||
|
|
||
| The URI has no USR fragment. The position is returned separately and |
There was a problem hiding this comment.
I don’t think that we have any URIs with USR fragments anymore, right? So this is a moot point.
Same below.
| for name in req.names { | ||
| if Task.isCancelled { return [:] } | ||
| var symbols: [SymbolOccurrence] = [] | ||
| _ = orLog("getting symbol information") { |
There was a problem hiding this comment.
orLog strings typically start with an uppercase letter in the codebase.
| guard | ||
| case .uri(let uriOnly) = symbol.location, | ||
| let urlComponents = URLComponents(url: uriOnly.uri.arbitrarySchemeURL, resolvingAgainstBaseURL: false), | ||
| let fullModuleName = urlComponents.queryItems?.last(where: { $0.name == "module" })?.value |
There was a problem hiding this comment.
As I said in the overview documentation, I think it would be nicer if we stored all information that’s used for the resolve requests in data instead of getting some information from data and some from the URI. Also so we don’t need to parse the dot-separated module name again.
There was a problem hiding this comment.
We don't parse the dot-separated module name in workspace/symbolInfo. we use symbolOccurrence.location.moduleName as-is
urlComponents.queryItems = [
URLQueryItem(name: "module", value: symbolOccurrence.location.moduleName)
]
| let moduleFileURI = DocumentURI( | ||
| { | ||
| var components = urlComponents | ||
| components.fragment = nil |
There was a problem hiding this comment.
Remind me, do we set the fragment anywhere anymore?
There was a problem hiding this comment.
No, this is "just in case".
There was a problem hiding this comment.
I’d personally just keep it simple and remove it. There are plenty other cases where we also don’t remove fragments “just in case”. This code just makes me start to wonder what would add a fragment.
| throw ResponseError.requestFailed( | ||
| "No source file found that imports \(fullModuleName); cannot open generated interface" | ||
| ) |
There was a problem hiding this comment.
Doesn’t have to be in this PR but I think we’d hit this if, say, a user used to import CryptoKit in their code base, then removes the import but CryptoKit is now still present in the index. Should we be able to open the module using fallback build settings in that case? Though we’d need to figure out how exactly those fallback builds settings should get computed.
There was a problem hiding this comment.
Yeah, that's going to be a nice improvement.
| // so its symbols are never written to any index store. | ||
| try await withTestScratchDir { binaryModuleDir in | ||
| let sourceFile = binaryModuleDir.appendingPathComponent("BinaryLib.swift") | ||
| try "public struct BinaryOnlyStruct {}".write(to: sourceFile, atomically: true, encoding: .utf8) |
There was a problem hiding this comment.
Use writeWithRetry here. Windows file writing is sometimes surprisingly finicky.
- Rename to "Jump to Definition for SDK Symbols.md" and update the H1 - Reflow prose to ~120 columns - Remove outdated notes in §5 (USR fragment, scroll-without-getReferenceDocument)
…cement - Reflow all prose to ~120 columns - Note that workspaceSymbol/resolve replaces location.uri (not just adds a range), which goes beyond what LSP 3.17 specifies
|
swiftlang/indexstore-db#287 |
|
@swift-ci Please test |
sourcekit/workspace/symbolNames— returns a flat, deduplicated list of every symbol name in the workspace index (source and indexed system modules). Clients use this to drive their search UI locally.sourcekit/workspace/symbolInfo— given a list of exact symbol names, returnsWorkspaceSymbolItemfor each occurrence across all workspaces, for display in the search result list. Source-file symbols getSymbolInformationwith afile://location. SDK/stdlib symbols get aWorkspaceSymbolwithlocation: .uri(…)The client must callworkspaceSymbol/resolveafter the user selects an SDK/stdlib symbol to obtain the concrete interface location.workspaceSymbol/resolve— resolves the deferredWorkspaceSymbollocation fromsourcekit/workspace/symbolInfo. Parses the?module=value intomoduleName/groupName, finds a real source file viamainFiles(containing:), callsopenGeneratedInterface, and returns the symbol withlocationreplaced by a fullsourcekit-lsp://generated-swift-interface/URI + range (or a tempfile://path for clients withoutworkspace/getReferenceDocumentsupport).